﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml;

namespace RTF2XML
{
    /// <summary>
    /// Serves as a base class for all control types, but also gets the control types of input controls.
    /// </summary>
    /// It's possible that this class can be abstracted, but I ran into problems when I tried to call methods at runtime.
    /// Unless you have a good reason, you should always use a single instance of this class to get the control types,
    /// this way you don't have to go through all the control classes and check each type every time.
    public class Control : GroupItem
    {
        private string sControl = "";

        /// <summary>
        /// Not actually used except as placeholders for the inherited classes
        /// </summary>
        public static List<string> lstControls;
        public static List<string> lstNumberedControls;
        public static List<string> lstDestinationControls;

        /// <summary>
        /// Used to hold a list of each regular control name and the class it belongs to
        /// </summary>
        private Dictionary<string, Type> dicControlTypes = new Dictionary<string, Type>();
        /// <summary>
        /// Used to hold a list of each numbered control name and the class it belongs to
        /// </summary>
        private Dictionary<string, Type> dicNumberedControlTypes = new Dictionary<string, Type>();

        /// <summary>
        /// Creates an instance of the Control class.
        /// This populates the dictionaries of controls and their types and allows for faster processing of controls
        /// </summary>
        public Control()
        {
            #region Get classes that inherit from the Control class
            //A list of all classes that inherit from the Control class
            List<Type> lstControlTypes = new List<Type>();

            //The code below means that any new control classes added to the project are
            //automatically found and used

            Assembly assembly = Assembly.GetAssembly(typeof(Control));
            //Get all classes in the assembly
            Type[] types = assembly.GetTypes();
            //Check each class
            foreach (Type type in types)
            {
                //If the class inherits from the control class
                if (type.BaseType == typeof(Control))
                {
                    //Add to the list of Control type classes
                    lstControlTypes.Add(type);
                }
            }
            #endregion

            #region Populate dictionaries of controls and their types
            this.dicControlTypes.Clear();
            //Get the list of regular and destination controls in each type
            foreach (Type type in lstControlTypes)
            {
                //For each regular control
                foreach (string control in (List<string>)type.InvokeMember("lstControls", BindingFlags.GetField, null, type, null))
                {
                    //Add the control and it's type to the dictionary
                    this.dicControlTypes.Add(control, type);
                }

                //For each destination control
                //For parsing purposes, destination controls are the same as regular controls
                foreach (string control in (List<string>)type.InvokeMember("lstDestinationControls", BindingFlags.GetField, null, type, null))
                {
                    //Add the control and it's type to the dictionary
                    this.dicControlTypes.Add(control, type);
                }
            }

            this.dicNumberedControlTypes.Clear();
            //Get the list of numbered controls in each type
            foreach (Type type in lstControlTypes)
            {
                //For each numbered control
                foreach (string control in (List<string>)type.InvokeMember("lstNumberedControls", BindingFlags.GetField, null, type, null))
                {
                    //Add the control and it's type to the dictionary
                    this.dicNumberedControlTypes.Add(control, type);
                }
            }
            #endregion
        }

        public Control(string sControl)
        {
            this.sControl = sControl;
        }

        /// <summary>
        /// Finds the type of control the input control is
        /// </summary>
        /// <param name="sControl">Control to find</param>
        /// <returns>Returns the control class the controls belongs to, null if no class is found</returns>
        public static Control GetTypeOfControl(string sControl)
        {
            #region Get classes that inherit from the Control class
            //A list of all classes that inherit from the Control class
            List<Type> lstControlTypes = new List<Type>();

            //The code below means that any new control classes added to the project are
            //automatically found and used

            Assembly assembly = Assembly.GetAssembly(typeof(Control));
            //Get all classes in the assembly
            Type[] types = assembly.GetTypes();
            //Check each class
            foreach (Type type in types)
            {
                //If the class inherits from the control class
                if (type.BaseType == typeof(Control))
                {
                    //Add to the list of Control type classes
                    lstControlTypes.Add(type);
                }
            }
            #endregion

            #region Check each control class for the input control
            //For each control class
            foreach (Type control in lstControlTypes)
            {
                //Check to see if the input control is part of the current control class
                if ((bool)control.InvokeMember("IsPartOfControl", BindingFlags.InvokeMethod, null, control, new object[] { sControl }))
                {
                    //Create a new instance of the control class with the input text
                    object o = Activator.CreateInstance(control, new object[]{sControl});
                    //Return the new control class
                    return (Control)o;
                }
            }
            #endregion

            //If no matching control class was found then return null
            return null;
        }

        /// <summary>
        /// Finds the type of control the input control is.
        /// This method uses the dictionaries and is much faster.
        /// </summary>
        /// <param name="sControl">Control to find</param>
        /// <returns>Returns the control class the controls belongs to, null if no class is found</returns>
        public Control GetControlType(string sControl)
        {
            //If the input control is a regular or destination control
            if (this.dicControlTypes.ContainsKey(sControl))
            {
                //Get the type of control
                Type control = this.dicControlTypes[sControl];

                //Create a new instance of the control with the input text
                object o = Activator.CreateInstance(control, new object[] { sControl });
                //Return the new control instance
                return (Control)o;
            }
            //Check to see if the control is a numbered control
            //There may be a better, faster way to do this that uses the dictionary better, but this is all I could think of at the time
            else
            {
                //For each numbered control
                foreach (KeyValuePair<string, Type> kv in this.dicNumberedControlTypes )
                {
                    //Get the selected control
                    string numcontrol = kv.Key;
                    //If the input text starts with the selected control
                    if (sControl.StartsWith(numcontrol))
                    {
                        //Try to parse out the number from the text
                        string number = sControl.Replace(numcontrol, "");
                        int n = 0;
                        if (int.TryParse(number, out n))
                        {
                            //If you are able to parse out the number then you have a valid control
                            //Get the control type
                            Type control = kv.Value;

                            //Create a new instance of the control with the input text
                            object o = Activator.CreateInstance(control, new object[] { sControl });
                            //Return the new control instance
                            return (Control)o;
                        }
                    }
                }
            }

            //If no matching control was found then return null
            return null;
        }

        /// <summary>
        /// Overrides the inherited AddXML method but does not use it.
        /// All inherited controls from this class should implement this method.
        /// </summary>
        /// <param name="xmlParentNode"></param>
        public override void AddXML(ref XmlElement xmlParentNode)
        {
        }
    }
}
